import re
import sys
import time
import socket
import string

import ial
import numeric
import channel

from random import randint

class client:
	def __init__(self, parent):
		self.parent = parent
		self.config = parent.config

		if self.config["network"].has_key("SSL"):
			self.serverSSL 	= self.config["network"]["SSL"]
		else:
			self.serverSSL	= ""
		if self.config["network"].has_key("password"):
			self.serverPass = self.config["netowrk"]["password"]
		else:
			self.serverPass	= ""
		
		self.ircNick 			= self.config["nickname"]
		self.ircUser 			= self.config["email"] 
		self.ircRealname	= self.config["realname"] 

		self.sock 				= None #socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self.connected 		= False
		self.registered 	= False

		self.ial					= ial.ial(self)
		self.channel 			= channel.channel(self)
		self.debug 				= parent.debug
		self.numeric 			= numeric.numeric_events

	def rfc2812_line(self):
		return re.compile("^(:(?P<prefix>[^ ]+) +)?(?P<command>[^ ]+)( *(?P<argument> .+))?")
	def rfc2812_sep(self):
		return re.compile("\r\n")
	def rfc2812_channel(self):
		return re.compile("^\#|\+|\!|\&[\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x2B]+$")
	def rfc2812_mask(self):
		return re.compile("^(?P<nick>[a-zA-Z\x5B-\x60\x7B-\x7D][a-zA-Z0-9\x5B-\x60\x7B-\x7D]*)!(?P<email>[a-zA-Z0-9]*)@(?P<hostname>[a-zA-Z0-9\2E\5F\2D]*)")
 
	def runEvent(self, event, parameters=[]):
		for i, cls in enumerate(self.parent.modules):
			for function in cls.events:
				if function == event:
					if event == "onRaw":
						cls.onRaw(numeric=parameters[0], command=parameters[1], mask=parameters[2], target=parameters[3], message=parameters[4])
					elif event == "onConnect":
						cls.onConnect()
					elif event == "onPing":
						cls.onPing(parameters[0])
					elif event == "onLogin":
						cls.onLogin()
					elif event == "onQuit":
						cls.onQuit(parameters[0])
					elif event == "onPrivmsg":
						if len(parameters) == 3:
							cls.onPrivmsg(parameters[0], parameters[1], parameters[2])
						else:
							cls.onPrivmsg(parameters[0], parameters[1])
					elif event == "onAction":
						if len(parameters) == 3:
							cls.onAction(parameters[0], parameters[1], parameters[2])
						else:
							cls.onAction(parameters[0], parameters[1])
					elif event == "onCTCP":
						cls.onCTCP(parameters[0], parameters[1], parameters[2])
					elif event == "onJoin":
						cls.onJoin(parameters[0], parameters[1])
					elif event == "onOp":
						cls.onOp(parameters[0], parameters[1], parameters[2])
					elif event == "onDeop":
						cls.onDeop(parameters[0], parameters[1], parameters[2])
					elif event == "onVoice":
						cls.onVoice(parameters[0], parameters[1], parameters[2])
					elif event == "onDevoice":
						cls.onDevoice(parameters[0], parameters[1], parameters[2])
					elif event == "onMode":
						print parameters
						if len(parameters) == 3:
							cls.onMode(parameters[0], parameters[1], parameters[2])
						else:
							cls.onMode(parameters[0], parameters[1], parameters[2], parameters[3])
					
					else:
						print event
						return False
		return True

	def connect(self, random=0):
		if self.connected:
			self.quit("Changing Servers")
		
		self.connected = False
		self.registered = False
		iterservers = int(0)
		while self.connected <> True:
			if iterservers == len(self.config["networkinfo"]["servers"]):
				self.debug.error("Couldn't connect to all registered servers")
				return False

			if random == 0:
				server = self.config["networkinfo"]["servers"][iterservers]
			else:
				# if random is defined we must wait for the timeout
				rd_max = len(self.config["networkinfo"]["servers"])
				server = self.config["networkinfo"]["servers"][randint(0, rd_max)]
				
				timeout = int(self.config["networkinfo"]["servers"]["timeout"])
				if timeout > 0:
					self.debug.info("Retrying to connect in %ds", timeout)
					time.sleep(timeout)

			self.debug.info("Connecting to %s (%s:%s)", server["name"], server["host"], server["port"])
	
			try:
				self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
				self.sock.connect((server["host"], int(server["port"])))
				self.connected = True
				self.debug.info("Connected!")
			except socket.error, e:
				iterservers += 1
				errno, string = e
				self.debug.warning("Couldn't connect: %s (%d)", string, int(errno))

				timeout = int(self.config["networkinfo"]["servers"]["timeout"]) 
				if timeout > 0:
					self.debug.info("Retrying to connect in %ds", timeout) 
					time.sleep(timeout)

		self.login()
		self.runEvent("onConnect")
		return True

	def send(self, data):
		if self.connected:
			self.sock.send(data + "\r\n")
			return True
		return False

	def sendraw(self, data):
		if self.connected:
			self.sock.send(data)
			return True
		return False

	def quit(self, reason=""):
		if reason == "":
			reason = "enfanhosado!"

		if self.connected:
			self.send("QUIT :" + reason)
			self.disconnect(forced=1)
			self.runEvent("onQuit", [reason])
			return True
		return False
	
	def disconnect(self, random=0, forced=0):
		if self.connected:
			try:
				self.sock.shutdown(socket.SHUT_RDWR)
				self.sock.close()
			except socket.error, e:
				errno, string = e
				self.debug.warning("Socket Error: %s (%d)", string, int(errno))

			self.connected = False
			self.runEvent("onDisconnect")

			if self.config["network"]["reconnect"] == "1" and not forced:
				self.connect(random)
			return True
		return False

	def password(self):
		if self.connected:
			self.send("PASS " + self.serverPass)
			return True
		return False

	def nick(self, nick=""):
		if self.connected:
			if nick == "": nick = self.ircNick
			self.send("NICK " + nick)
			self.ircNick = nick
			return True
		return False

	def user(self): 
		if self.connected and not self.registered:
			user = self.ircUser
			real = self.ircRealname
			
			# +iw (8+4)
			mode = "12"

			self.send("USER " + user + " " + mode + " * :" + real)
			return True
		return False

	def login(self):
		if self.connected and not self.registered:
			if self.serverPass != "":
				self.password()
			self.nick()
			self.user()
			self.registered = True
			return True
		return False

	def pong(self, code):
		if self.connected:
			self.send("PONG :" + code)
			return True
		return False

	def join(self, channel):
		if self.connected and self.registered and self.channel.isChannel(channel):
			self.send("JOIN " + channel)
			return True
		return False

	def part(self, channel):
		if self.connected and self.registered and channel.isChannel(channel):
			for chan in self.channel.chanlist:
				if chan == channel:
					self.send("PART " + channel)
					self.channel.rem(channel)
					return True
		return False

	def privmsg(self, dest, msg):
		if self.connected and self.registered:
			self.send("PRIVMSG " + dest + " :" + msg)
			return True
		return False

	def notice(self, dest, msg):
		self.send("NOTICE " + dest + " :" + msg)
		return True

	def ctcpReply(self, dest, ctcp, msg):
		if self.connected and self.registered:
			self.notice(dest, "\001" + ctcp + " " + msg + "\001")
			return True
		return False

	def kick(self, channel, nick, msg=""):
		if self.connected and self.registered and self.inChannel(channel):
			self.send("KICK " + channel + " " + nick + " " + msg)
			return True
		return False

	def parser(self):
		buffer = ""
		keepbuf = True

		while True:
			try:
				data = self.sock.recv(8192)
			except socket.error, e:
				errno, string = e
				self.debug.warning("Disconnected: %s (%d)", string, int(errno))
				self.disconnect()
			except:
				self.disconnect(forced=1)
				sys.exit(0)

			if not data:
				# Couldn't read anything, connection down
				self.debug.warning("Disconnected: Connection down")

				# If the next connection is the same server it will go right down
				#  here so we must avoid this, by connecting to a random server
				self.disconnect(random=1)

			lines = self.rfc2812_sep().split(buffer + data)
	
			# Keep buffer
			for i in lines[-1].split("\n"):
				if i == "":
					keepbuf = False
					break
	
			if keepbuf:
				buffer = lines[-1]
				lines = lines[:-1]
	
			for line in lines:
				if not line:
					continue

				prefix = None
				command = None
				arguments = None
		
				self.debug.info(line)
				line = line.decode("latin-1")
				line = line.encode("utf-8")
				m = self.rfc2812_line().match(line)
				if m != None:
					if m.group("prefix"):
						prefix = m.group("prefix")

					if m.group("command"):
						command = m.group("command").lower()

					if m.group("argument"):
						args = m.group("argument").split(" :", 1)
						arguments = args[0].split()
						target = args[0].split()
						if len(args) == 2:
							arguments.append(args[1])
							message = args[1]

				# Translate numerics into strings
				if command in self.numeric:
					self.runEvent("onRaw", [command, self.numeric[command], prefix, target, message])
					command = self.numeric[command]

				if command == "welcome":
					self.runEvent("onLogin")
					for channel in self.config["network"]["channels"]:
						self.join(channel)

				elif command == "namreply":
					channel = target[2]

					if channel not in self.channel.chanlist.keys():
						self.channel.chanlist[channel] = []

					for nick in message.strip().split(" "):
						if nick[0] == '+' or nick[0] == '@':
							tuple = nick[0], nick[1:]
						tuple = nick
						self.channel.addNick(channel, tuple)

				elif command == "endofnames":
					pass

				elif command == "ping":
					self.pong(arguments[0])
					self.runEvent("onPing", [target])

				elif command == "notice":
					ctcp = ""

					# Notice
					if '\001' not in message:
						if target[0] == "#":
							self.runEvent("onNotice", [prefix, message, target[0]])
						else:
							self.runEvent("onNotice", [prefix, message])
					# CTCP
					else:
						m = message.split("\001")[1]
						ctcp = m.split(" ")[0]
						if len(m.split(" ")) > 1:
							message = m.split(" ")[1]
						else:
							message = None

						self.runEvent("onCTCP", [prefix, ctcp, message])

				elif command == "privmsg":
					if target[0][0] == "#":
						self.runEvent("onPrivmsg", [prefix, message, target[0]])
					else:
						self.runEvent("onPrivmsg", [prefix, message])

				elif command == "join":
					self.runEvent("onJoin", [prefix, target])
				elif command == "part":
					self.runEvent("onPart", [prefix, target[0], message])
				elif command == "quit":
					self.runEvent("onQuit", [prefix, message])
				elif command == "kick":
					self.runEvent("onKick", [prefix, target[0], target[1], message])
				elif command == "nick":
					self.runEvent("onNick", [prefix, target[0]])

				elif command == "mode":
					if target[0][0] == '#':
						channel = target[0]
						modes = target[1]
						dmodes = "ovblk"
			
						u = int(1)
						for mode in modes:
							if mode == '+' or mode == '-':
								state = mode

							if mode in dmodes:
								u += 1
								if state+mode == '+o':
									self.runEvent("onOp", [prefix, channel, target[u]])
								elif state+mode == '-o':
									self.runEvent("onDeop", [prefix, channel, target[u]])
								elif state+mode == '+v':
									self.runEvent("onVoice", [prefix, channel, target[u]])
								elif state+mode == '-v':
									self.runEvent("onDevoice", [prefix, channel, target[u]])
								elif state+mode == '+b':
									self.runEvent("onBan", [prefix, channel, target[u]])
								elif state+mode == '-b':
									self.runEvent("onUnban", [prefix, channel, target[u]])
								else:
									if state == '+':
										self.runEvent("onMode", [prefix, channel, state + mode, target[u]])
									if state == '-':
										u -= 1
										self.runEvent("onMode", [prefix, channel, state + mode])
							# not in dmodes
							elif mode.isalpha():
								self.runEvent("onMode", [prefix, channel, state + mode])
